package c17网络编程;

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import org.junit.Test;

public class 多线程下载 {
	@Test
	public void download() throws Exception {
		String path = "http://apk.gfan.com/index.php?c=api&m=down&src=wap&apk=67340";
		URL url = new URL(path);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setConnectTimeout(5 * 1000);
		conn.setRequestMethod("GET");
		conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
		conn.setRequestProperty("Accept-Language", "zh-CN");
		conn.setRequestProperty("Charset", "UTF-8");
		conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
		conn.setRequestProperty("Connection", "Keep-Alive");
		System.err.println(conn.getResponseCode());

		int filesize = conn.getContentLength();//得到文件大小
		conn.disconnect();
		int threadNum = 3;//线程数
		//必须+1，因为调用seek(i)方法是将文件指针定位到下标i处，然后开始写文件
		int perthreadsize = filesize / threadNum + 1;
		RandomAccessFile file = new RandomAccessFile("juli.apk", "rw");
		file.setLength(filesize);//设置本地文件的大小
		file.close();
		for (int i = 0; i < threadNum; i++) {
			int startpos = i * perthreadsize;//计算每条线程的下载位置
			RandomAccessFile perthreadfile = new RandomAccessFile("juli.apk", "rw");//
			perthreadfile.seek(startpos);//从文件的什么位置开始写入数据
			new DownladerThread(i, path, startpos, perthreadsize, perthreadfile).start();
		}
		//以下代码要求用户输入q才会退出测试方法，如果没有下面代码，会因为进程结束而导致进程内的下载线程被销毁
		int quit = System.in.read();
		while ('q' != quit) {
			Thread.sleep(2 * 1000);
		}
	}

	private class DownladerThread extends Thread {
		private int startpos;//从文件的什么位置开始下载
		private int perthreadsize;//每条线程需要下载的文件大小
		private String path;
		private RandomAccessFile file;
		private int threadid;

		public DownladerThread(int threadid, String path, int startpos, int perthreadsize, RandomAccessFile perthreadfile) {
			this.path = path;
			this.startpos = startpos;
			this.perthreadsize = perthreadsize;
			this.file = perthreadfile;
			this.threadid = threadid;
		}

		@Override
		public void run() {
			try {
				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(5 * 1000);
				conn.setRequestMethod("GET");
				conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
				conn.setRequestProperty("Accept-Language", "zh-CN");
				conn.setRequestProperty("Charset", "UTF-8");
				//使用Http的Range头字段指定每条线程从文件的什么位置开始下载，下载到什么位置为止
				conn.setRequestProperty("Range", "bytes=" + this.startpos + "-");//不用指定结束位置
				InputStream inStream = conn.getInputStream();
				byte[] buffer = new byte[1024];
				int len = 0;
				int length = 0;
				while (length < perthreadsize && (len = inStream.read(buffer)) != -1) {
					file.write(buffer, 0, len);
					length += len;//累计该线程下载的总大小
				}
				file.close();
				inStream.close();
				System.err.println(threadid + "线程完成下载");
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws Exception {
		new 多线程下载().download();
	}
}

